﻿/*************************************************************************
 * Created by OhZee Creative, LLC
 * 2013
 * This code is licensed under Microsoft Public License (Ms-PL)
 * http://opensource.org/licenses/ms-pl
 * http://ozframework.codeplex.com/
 *************************************************************************/

namespace OhZeeCreative.Helpers
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using Windows.UI.Xaml;

	public static class ReflectionHelper
	{
		public static IEnumerable<Type> GetDerivedClasses(this Type baseType, bool instantiableOnly = false)
		{
			TypeInfo baseTypeInfo = baseType.GetTypeInfo();
			Assembly assembly = baseTypeInfo.Assembly;

			foreach (Type type in assembly.ExportedTypes)
			{
				TypeInfo typeInfo = type.GetTypeInfo();

				if (typeInfo.IsPublic && baseTypeInfo.IsAssignableFrom(typeInfo) && type != baseType && (!instantiableOnly || !typeInfo.IsAbstract))
				{
					yield return type;
				}
			}
		}
		
		/// <summary>
		/// Determines whether the derivedType is a derived type of baseType. If you have instances, use the 'as' operator instead.
		/// </summary>
		/// <param name="derivedType"></param>
		/// <param name="baseType"></param>
		/// <returns></returns>
		public static bool DerivesFrom(this Type derivedType, Type baseType)
		{
			if (baseType.Equals(derivedType))
			{
				return true;
			}

			TypeInfo baseTypeInfo = baseType.GetTypeInfo();
			TypeInfo derivedTypeInfo = derivedType.GetTypeInfo();

			return baseTypeInfo.IsAssignableFrom(derivedTypeInfo);
		}

		public static object GetPropertyValue(object instance, string propertyName)
		{
			PropertyInfo propertyInfo = instance.GetType().GetRuntimeProperty(propertyName);

			if (propertyInfo == null || !propertyInfo.CanRead)
			{
				throw new ArgumentException("Specified property doesn't exist or isn't readable");
			}

			return propertyInfo.GetValue(instance);
		}

		/// <summary>
		/// Sets the specified property on the instance to the given value
		/// </summary>
		/// <param name="instance">The object that owns the property</param>
		/// <param name="propertyName">The name of the property to be set</param>
		/// <param name="value">The value must be of the type expected by the property</param>
		public static void SetPropertyValue(object instance, string propertyName, object value)
		{
			PropertyInfo propertyInfo = instance.GetType().GetRuntimeProperty(propertyName);

			if (propertyInfo == null || !propertyInfo.CanWrite)
			{
				throw new ArgumentException("Specified property doesn't exist or isn't writable");
			}

			DebugHelper.Assert(value == null || value.GetType().DerivesFrom(propertyInfo.PropertyType));
			
			propertyInfo.SetValue(instance, value);			
		}

        public static bool ClearDependencyProperty(DependencyObject instance, string propertyName)
        {
            if (instance != null)
            {
                DependencyProperty property = GetPropertyAsDependencyProperty(instance, propertyName);
                if (property != null)
                {
                    instance.ClearValue(property);
                    return true;
                }
            }
            return false;
        }

        public static DependencyProperty GetPropertyAsDependencyProperty(this DependencyObject instance, string regularPropertyName, bool recurseUp = true)
        {
            string dependencyPropertyName = regularPropertyName + "Property";
            Type type = instance.GetType();

            return GetDependencyProperty(type, dependencyPropertyName, recurseUp);
        }

        public static DependencyProperty GetDependencyProperty(Type type, string dependencyPropertyName, bool recurseUp = true)
		{
			// If the dependency property is on the type, access it as a field
			FieldInfo fieldInfo = type.GetRuntimeField(dependencyPropertyName);

			if (fieldInfo != null && fieldInfo.IsPublic && fieldInfo.IsStatic && fieldInfo.FieldType == typeof(DependencyProperty))
			{
				return (DependencyProperty)fieldInfo.GetValue(null);
			}

			if (recurseUp)
			{
				// otherwise, access it as a property
				do  
				{
					PropertyInfo propertyInfo = type.GetRuntimeProperty(dependencyPropertyName);

					if (propertyInfo != null && propertyInfo.PropertyType == typeof(DependencyProperty))
					{
						return (DependencyProperty)propertyInfo.GetValue(null);
					}
					type = type.GetTypeInfo().BaseType;
				}
				while  (type != null);
			}

			return null;
		}

		public static Type GetPropertyType(this Type type, string name)
		{
			return type.GetRuntimeProperty(name).PropertyType;
		}

    }
}
